#include "fixedmodel.h"
#include "diutils.h"

#include <QInputDialog>

FixedModel::FixedModel(const QString& fileName, QWidget *parent)
    : ProtocolTableModel(parent),
      file(fileName),
      recordSize(0),
      recordCount(0),
      showCount(0)
{
}

void FixedModel::loadRecord(int row) const
{
    if (row == cacheRow)
        return;

    QFileGuard fg(file, QIODevice::ReadOnly);
    if (fg.isOpened()) {
        file.seek(row * recordSize);
        record = file.read(recordSize);
    }

    cacheRow = row;
}

int FixedModel::rowCount(const QModelIndex &parent) const
{
    return parent.isValid() ? 0 : showCount;
}

int FixedModel::columnCount(const QModelIndex &parent) const
{
    return parent.isValid() ? 0 : recordSize + 1;
}

QVariant FixedModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();

    if (role == Qt::DisplayRole) {
        int row = index.row();
        int col = index.column();
        if (row > recordCount || row < 0)
            return QVariant();

        if (0 == col)
            return recordSize;
        else {
            loadRecord(row);
            return toHex(record.mid(col - 1, 1), 0);
        }
    }

    return QVariant();
}

void FixedModel::fetchMore(const QModelIndex &parent)
{
    if (parent.isValid())
        return;

    QFileGuard fg(file, QIODevice::ReadOnly);
    if (!fg.isOpened())
        return;

    int count = recordCount - showCount;
    if (count > 100)
        count = 100;

    if (count <= 0)
        return;

    beginInsertRows(QModelIndex(), showCount, showCount + count - 1);
    showCount += count;
    endInsertRows();
}

bool FixedModel::canFetchMore(const QModelIndex &parent) const
{
    if (parent.isValid())
        return false;

    return showCount < recordCount;
}

QVariant FixedModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if (role == Qt::DisplayRole) {
        if (orientation == Qt::Horizontal) {
            if (section == 0)
                return tr("Bytes");
            else
                return section;
        } else {
            return section + 1;
        }
    }

    return QVariant();
}

qint64 FixedModel::position(const QModelIndex &index)
{
    int row = index.row();
    int col = index.column();
    return row * recordSize + col - 1;
}

QModelIndex FixedModel::locate(qint64 pos)
{
    return index(pos / recordSize, pos % recordSize + 1);
}

bool FixedModel::deleteRows(const QString &fileName, const QModelIndexList &rows)
{
    if (!file.open(QIODevice::ReadOnly))
        return false;
    QFileGuard fg1(file);

    QFile out(fileName);
    if (!out.open(QIODevice::ReadWrite))
        return false;
    QFileGuard fg2(out);

    QMap<int, QModelIndex> delRows;
    for (QModelIndexList::const_iterator it = rows.begin(); it != rows.end(); it++) {
        QModelIndex index = *it;
        delRows.insert(index.row(), index);
    }

    QProgressDialog progress(tr("Save as..."), tr("Cancel"), 0, recordCount, qApp->activeWindow(), Qt::WindowStaysOnTopHint);
    progress.setWindowTitle(tr("Progress"));
    progress.show();

    for (int i = 0; i < recordCount; i++) {
        QByteArray data = file.read(recordSize);
        if (!delRows.contains(i)) {
            out.write(data);
        }
        progress.setValue(i);
        qApp->processEvents();
        if (progress.wasCanceled())
            return false;
    }

    return true;
}

bool FixedModel::deleteColumns(const QString &fileName, const QModelIndexList &cols)
{
    if (!file.open(QIODevice::ReadOnly))
        return false;
    QFileGuard fg1(file);

    QFile out(fileName);
    if (!out.open(QIODevice::ReadWrite))
        return false;
    QFileGuard fg2(out);

    QMap<int, QModelIndex> delCols;
    for (QModelIndexList::const_iterator it = cols.begin(); it != cols.end(); it++) {
        int col = it->column();
        if (col)
            delCols.insert(col, *it);
    }

    QProgressDialog progress(tr("Save as..."), tr("Cancel"), 0, recordCount, qApp->activeWindow(), Qt::WindowStaysOnTopHint);
    progress.setWindowTitle(tr("Progress"));
    progress.show();

    for (int i = 0; i < recordSize; i++) {
        QByteArray data = file.read(recordSize);
        for (int i = 0; i < data.size(); i++) {
            if (delCols.contains(i+1))
                continue;
            out.write(data.mid(i, 1));
        }

        progress.setValue(i);
        qApp->processEvents();
        if (progress.wasCanceled())
            return false;
    }

    return true;
}

bool FixedModel::saveRows(const QString &fileName, const QModelIndexList &rows)
{
    if (!file.open(QIODevice::ReadOnly))
        return false;
    QFileGuard fg1(file);

    QFile out(fileName);
    if (!out.open(QIODevice::ReadWrite))
        return false;
    QFileGuard fg2(out);

    QProgressDialog progress(tr("Save seleted rows as..."), tr("Cancel"), 0, rows.size(), qApp->activeWindow());
    progress.setWindowTitle(tr("Progress"));
    progress.setModal(true);
    int step = 0;
    for (QModelIndexList::const_iterator it = rows.begin(); it != rows.end(); it++) {
        QModelIndex index = *it;
        int row = index.row();
        qint64 from = recordSize * row;
        file.seek(from);
        out.write(file.read(recordSize));
        progress.setValue(++step);
        qApp->processEvents();
        if (progress.wasCanceled())
            return false;
    }

    return true;
}

bool FixedModel::saveColumns(const QString &fileName, const QModelIndexList &cols)
{
    if (!file.open(QIODevice::ReadOnly))
        return false;
    QFileGuard fg1(file);

    QFile out(fileName);
    if (!out.open(QIODevice::ReadWrite))
        return false;
    QFileGuard fg2(out);

    QMap<int, QModelIndex> selectCols;
    for (QModelIndexList::const_iterator it = cols.begin(); it != cols.end(); it++)  {
        QModelIndex index = *it;
        selectCols.insert(index.column(), index);
    }
    QProgressDialog progress(tr("Save selected colnums as..."), tr("Cancel"), 0, recordCount, qApp->activeWindow());
    progress.setWindowTitle(tr("Progress"));
    progress.setModal(true);

    for (int i = 0; i < recordCount; i++) {
        QByteArray data = file.read(recordSize);
        for (int i = 0; i < data.size(); i++) {
            if (selectCols.contains(i))
                out.write(data.mid(i, 1));
        }

        progress.setValue(i);
        qApp->processEvents();
        if (progress.wasCanceled())
            return false;
    }

    return true;
}

bool FixedModel::supportFilter(const QModelIndex &index)
{
    Q_UNUSED(index);
    return false;
}

void FixedModel::filter(const QModelIndex &index)
{
    Q_UNUSED(index);
}

void FixedModel::setFrameSize(int size)
{
    beginInsertColumns(QModelIndex(), 0, size - 1);
    recordSize = size;
    endInsertColumns();

    if (recordSize > 0) {
        recordCount = file.size() / recordSize;
        if (file.size() % recordSize)
            recordCount++;
    } else {
        recordSize = 0;
    }
}

